package com.security.control.browser.config;

import com.security.control.core.authentication.FormAuthenticationConfig;
import com.security.control.core.authentication.mobile.SmsValidateCodeAuthenticationSecurityConfig;
import com.security.control.core.authorize.AuthorizeConfigManager;
import com.security.control.core.properties.SecurityProperties;
import com.security.control.core.support.SecurityConstants;
import com.security.control.core.validate.config.ValidateCodeSecurityConfig;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.social.security.SpringSocialConfigurer;

import java.util.Set;

/**
 * @author imoot@gamil.com
 * @date 2018/12/17 0017 11:53
 * --springsecurity为一组过滤器链
 */
//@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

    //注入自定义properties读取类
    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private UserDetailsService myUserDetailsService;

    @Autowired
    private SmsValidateCodeAuthenticationSecurityConfig smsValidateCodeAuthenticationSecurityConfig;

    //注入验证码校验配置类
    @Autowired
    private ValidateCodeSecurityConfig validateCodeSecurityConfig;

    @Autowired
    private SpringSocialConfigurer mySocialSecurityConfig;

    @Autowired
    private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;//注入并发登录导致session失效的处理类

    @Autowired
    private InvalidSessionStrategy invalidSessionStrategy;//注入session失效的处理类

    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;

    @Autowired
    private AuthorizeConfigManager authorizeConfigManager;

    @Autowired
    private FormAuthenticationConfig formAuthenticationConfig;

    /*@Autowired
    private Set<BrowserSecurityConfigCallback> configCallbacks;*/

    @Autowired
    private PersistentTokenRepository persistentTokenRepository;

    @Autowired
    private AuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Autowired
    private AuthenticationFailureHandler myAuthenticationFailureHandler;

    /**
     * @param web
     * 配置web资源请求规则
     */
    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers(HttpMethod.GET, "/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg");
    }

    /**
     * @param http
     * @throws Exception
     * 配置http请求规则
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        validateCodeFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
        validateCodeFilter.setSecurityProperties(securityProperties);
        validateCodeFilter.afterPropertiesSet();*/

        /*if(CollectionUtils.isNotEmpty(configCallbacks)){
            configCallbacks.forEach(callback -> callback.config(http));
        }*/

        //formAuthenticationConfig.configure(http);

        http
                //.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)//把图片验证码校验过滤器添加在用户名密码校验过滤器之前
                .apply(validateCodeSecurityConfig)//把验证码校验配置在security过滤器链上
                .and()
                //.apply(smsValidateCodeAuthenticationSecurityConfig)//把短信校验配置在security过滤器链上
                //.and()
                .apply(mySocialSecurityConfig)//把social过滤器配置在security过滤器链上
                .and()
                //.httpBasic()//基本登录
                .formLogin()//表单登录（以下是对表单登录的设置）
                .loginPage(SecurityConstants.DEFAULT_SIGN_IN_PAGE_URL)//表单登录页面
                //.loginPage(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)
                .loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)//自定义登录请求接口
                .successHandler(myAuthenticationSuccessHandler)//设置自定义登录（鉴权）成功处理器
                .failureHandler(myAuthenticationFailureHandler)//设置自定义登录（鉴权）失败处理器
                .and()
                //.rememberMe()//以下是对记住我的设置
                //.tokenRepository(persistentTokenRepository)//设置数据库处理类
                //.tokenValiditySeconds(securityProperties.getBrowserProperties().getRememberMeSeconds())//设置失效时间
                //.userDetailsService(myUserDetailsService)//跳转到登录
                //.and()
                .sessionManagement()//以下是对session的设置
                //.invalidSessionUrl(securityProperties.getSessionProperties().getInvalidSessionUrl())//session失效跳转地址
                .invalidSessionStrategy(invalidSessionStrategy)//session失效的处理策略
                .maximumSessions(securityProperties.getSessionProperties().getMaximumSessions())//设置session可以创建的最大数量
                .maxSessionsPreventsLogin(securityProperties.getSessionProperties().getMaxSessionsPreventsLogin())//为true，则当session的登录数量达到最大值时，阻止当前登录；反之，则挤掉最早的一个登录用户
                .expiredSessionStrategy(sessionInformationExpiredStrategy)//并发登录导致session失效的处理策略（当最早的一个session被最近的一个session挤掉时，会触发处理类）
                .and()
                .and()
                .logout()//以下是对退出登录的设置
                .logoutUrl("/signOut")//设置退出登录请求的url
                //.logoutSuccessUrl("/static/browser_logout.html")//设置退出登录操作成功后重定向的url
                .logoutSuccessHandler(logoutSuccessHandler)//设置退出登录操作成功后的处理器（与url不能同时配置）
                .deleteCookies("JSESSIONID")//设置退出登录后清除的cookies
                //.and()
                //.authorizeRequests()//以下是对请求的授权设置（只区分登录和未登录）
                /*.antMatchers(
                        SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
                        securityProperties.getBrowserProperties().getSignInUrl(),
                        securityProperties.getBrowserProperties().getSignUpUrl(),
                        securityProperties.getBrowserProperties().getLogoutUrl(),
                        SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*",
                        SecurityConstants.DEFAULT_SOCIAL_AUTHENTICATION_URL + "/*",
                        securityProperties.getQqSocialProperties().getFilterProcessesUrl() + "/*",
                        SecurityConstants.DEFAULT_SIGN_UP_PAGE_URL,
                        SecurityConstants.DEFAULT_SESSION_INVALID_URL).permitAll()*///添加匹配器，去掉访问url需要的权限
                //.anyRequest()//任何请求
                //.authenticated()//都需要身份认证
                .and()
                .csrf()//以下是对跨站请求伪造的设置
                .disable()//停用跨站请求伪造防御
                .headers().frameOptions().disable();

        authorizeConfigManager.config(http.authorizeRequests());
    }
}
